package top.tangyh.lamp.care.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.util.StringUtil;
import groovy.lang.Lazy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.tangyh.basic.context.ContextUtil;
import top.tangyh.basic.exception.BizException;
import top.tangyh.basic.utils.DateUtils;
import top.tangyh.lamp.base.service.system.BaseRoleService;
import top.tangyh.lamp.care.entity.BGenItem;
import top.tangyh.lamp.care.entity.BGenItemDetail;
import top.tangyh.lamp.care.entity.BPatient;
import top.tangyh.lamp.care.mapper.BCareRecordMapper;
import top.tangyh.lamp.care.mapper.BPatientMapper;
import top.tangyh.lamp.care.service.*;
import top.tangyh.basic.base.service.impl.SuperServiceImpl;
import top.tangyh.lamp.care.manager.BCareRecordManager;
import top.tangyh.lamp.care.entity.BCareRecord;
import top.tangyh.lamp.care.vo.query.*;
import top.tangyh.lamp.care.vo.result.*;
import top.tangyh.lamp.care.vo.save.BCareRecordSaveVO;
import top.tangyh.lamp.care.vo.save.FanShenRecordInputDTO;
import top.tangyh.lamp.care.vo.update.BCareRecordUpdateVO;
import top.tangyh.lamp.care.vo.update.CareRecordDelInVO;
import top.tangyh.lamp.care.vo.update.UpdateCareRecordStatusVO;
import top.tangyh.lamp.common.constant.CommonConstant;
import top.tangyh.lamp.common.utils.ConvertUtil;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 护理操作记录表
 * </p>
 *
 * @author zqb
 * @date 2024-07-18 14:10:32
 * @create [2024-07-18 14:10:32] [zqb] [代码生成器生成]
 */
@Slf4j
@RequiredArgsConstructor
@Service
@Transactional(readOnly = true)
public class BCareRecordServiceImpl extends SuperServiceImpl<BCareRecordManager, Long, BCareRecord> implements BCareRecordService {

    private final BGenItemService bGenItemService;

    private final BGenItemDetailService bGenItemDetailService;

    private final BPatientMapper bPatientMapper;


    private final BTimePlanService bTimePlanService;

    private final BaseRoleService baseRoleService;

    private final BWorkPlanService bWorkPlanService;

    private final BCareRecordMapper bCareRecordMapper;




    String MODE_NAME_OF_CARECZ = "care-cz";
    String MODEL_NAME_TIME_AXIOS = "hlcz";

    /**
     * 时间格式，应用在返回时间轴的标题上
     */
    private final String TITLE_HH_MM = "HH:mm";

    /**
     * 时间格式，应用在返回时间轴的字段上
     */
    private final String FIELD_HH_MM = "HHmm";

    /**
     * 列是时间类型
     */
    private final String COL_TIME = "time";

    /**
     * 返回时不跨天前缀
     */
    private final String PRE_A = "A";
    /**
     * 返回时跨天前缀
     */
    private final String PRE_B = "B";

    /**
     * 返回记录查找值时行坐标索引
     */
    private final String ROW_INDEX = "rowIndex";

    /**
     * 列是字段串类型
     */
    private final String COL_TEXT = "text";
    /**
     * 项目名称字段名
     */
    private final String ITEM_NAME = "itemName";




    @Override
    public CareRecordQuery2AxiosDto queryBy2Axios(CareRecordQueryParam param) {
        List<Long> roIds = baseRoleService.findRoleIdByEmployeeId(ContextUtil.getEmployeeId());
        Long roleId = null;
        //这里这样处理的前提是 每个用户只能有一个角色，为了这个逻辑后台管理必须限制 一个用户只能绑定一个角色
        if (roIds.size() > 0) {
            roleId = roIds.get(0);
        }


        //参数检查
        checkParamOfQueryAxios(param);

        CareRecordQuery2AxiosDto rst = new CareRecordQuery2AxiosDto();
        GenItemQueryParam cfgParam = new GenItemQueryParam(MODE_NAME_OF_CARECZ, param.getPatientIdent(), CommonConstant.NO_NUMBER);
        Map<String, BGenItem> itemBaseAxios = bGenItemService.queryAllByPid(cfgParam.getModelCode(), cfgParam.getPatientIdent());

        if (StrUtil.isBlank(param.getHospitalNumber())) {
            BPatient pt = bPatientMapper.selectOneByPatIdent(param.getPatientIdent());
            if (pt != null && StrUtil.isNotBlank(pt.getHospitalNumber())) {
                param.setHospitalNumber(pt.getHospitalNumber());
            }
        }

        //时间点获取
        Set<TimePointOutVO> timeBaseAxios = null;
        if (param.getStartTime() != null || param.getEndTime() != null) {
            //CurrUserUtil.getCurrRole()).getId()


            timeBaseAxios = bTimePlanService.getAllTimePoitByPt(MODEL_NAME_TIME_AXIOS, roleId,
                    param.getPatientIdent(), param.getHospitalNumber(), param.getStartTime(), param.getEndTime());
        } else if (param.getShiftTime() != null && param.getShiftCode() != null) {
            timeBaseAxios = bWorkPlanService.getAllTimePoitByPtAndShift(
                    MODEL_NAME_TIME_AXIOS,
                    roleId,
                    param.getPatientIdent(),
                    param.getHospitalNumber(),
                    param.getShiftTime(),
                    Convert.toLong(param.getShiftCode()));
        }

        if (CollectionUtil.isEmpty(timeBaseAxios) || !timeBaseAxios.stream().findFirst().isPresent()) {
            log.info("------------ 获取不到时间轴的时间点，参数：{}", JSON.toJSONString(param));
            return rst;
        }
        long baseDate = DateUtil.beginOfDay(new Date(timeBaseAxios.stream().findFirst().get().getTimePoint())).getTime();
        List<CareRecordColVo> xAxisByTime = new ArrayList<>();
        Set<Long> timeAxios = new LinkedHashSet<>();

        //班次时间点
        TimePointOutVO temp = null;
        String rkstr = null;
        if (null != param.getRkTime()) {
            rkstr = DateUtils.format(new Date(param.getRkTime()), "yyyy-MM-dd HH") + ":00:00";
        }
        for (TimePointOutVO timePoint : timeBaseAxios) {
            Long time = timePoint.getTimePoint();
            String piontstr = DateUtils.format(new Date(time), "yyyy-MM-dd HH") + ":00:00";
            if (null != rkstr) {
                if (time < param.getRkTime()) {
                    if (piontstr.equals(rkstr)) {
                        temp = timePoint;
                    }
                    continue;
                }
            }
            boolean isSecondDay = baseDate != DateUtil.beginOfDay(new Date(time)).getTime();
            CareRecordColVo CareRecordColVo = getTimeCol(time, isSecondDay);
            CareRecordColVo.setIsUser(CommonConstant.NO_NUMBER);
            CareRecordColVo.setId(timePoint.getId());
            xAxisByTime.add(CareRecordColVo);
            timeAxios.add(CareRecordColVo.getMappedTime());
        }
        if (null != temp) {
            boolean isSecondDay = baseDate != DateUtil.beginOfDay(new Date(param.getRkTime())).getTime();
            CareRecordColVo CareRecordColVo = getTimeCol(param.getRkTime(), isSecondDay);
            CareRecordColVo.setIsUser(CommonConstant.NO_NUMBER);
            CareRecordColVo.setId(temp.getId());
            xAxisByTime.add(CareRecordColVo);
            timeAxios.add(CareRecordColVo.getMappedTime());
        }
        xAxisByTime = xAxisByTime.stream().sorted(Comparator.comparing(CareRecordColVo::getMappedTime)).collect(Collectors.toList());

        //生成返回数据的 二维空矩阵，y输为项目，X轴为时间
        Map<String, Map<String, CareRecordCellVo>> data = newMatrix(itemBaseAxios, timeAxios);
        List<CareRecordQueryDto> rdList = query(param);
        if (CollectionUtil.isNotEmpty(rdList)) {
            for (CareRecordQueryDto rd : rdList) {
                if (data.containsKey(rd.getItemName())) {
                    Map<String, CareRecordCellVo> row = data.get(rd.getItemName());
                    boolean isSecondDay = baseDate != DateUtil.beginOfDay(new Date(rd.getMappedTime())).getTime();
                    String timeKey = getTimeCode(rd.getMappedTime(), isSecondDay);
                    if (row.containsKey(timeKey)) {
                        CareRecordCellVo CareRecordCellVo = new CareRecordCellVo(COL_TIME, rd.getMappedTime(), rd.getItemValue());
                        CareRecordCellVo.setCreatedBy(rd.getCreatedBy());
                        CareRecordCellVo.setUpdatedBy(rd.getUpdatedBy());
                        CareRecordCellVo.setCreatedTime(rd.getCreatedTime());
                        CareRecordCellVo.setUpdatedTime(rd.getUpdatedTime());
                        row.put(timeKey, CareRecordCellVo);
                    }
                }
            }
        }


        //生成X轴
        List<CareRecordColVo> xAxis = new ArrayList<>();
        xAxis.add(new CareRecordColVo(COL_TEXT, "序号", ROW_INDEX, null));
        xAxis.add(new CareRecordColVo(COL_TEXT, "项目", ITEM_NAME, null));
        xAxis.addAll(xAxisByTime);
        rst.setXAxis(xAxis);

        //生成Y轴
        rst.setYAxios(itemBaseAxios.keySet().stream().map(itemBaseAxios::get).collect(Collectors.toList()));
        rst.setYAxios(rst.getYAxios().stream().sorted(Comparator.comparing(BGenItem::getIsGeneral).reversed())
                .sorted(Comparator.comparing(BGenItem::getOrderNo).reversed()).collect(Collectors.toList()));
        rst.setData(new ArrayList<>(data.values()));
        return rst;
    }

    /**
     * 查询返回二维数据
     *
     * @param param 查询参数
     * @return
     */
    @Override
    public List<CareRecordQueryDto> query(CareRecordQueryParam param) {
        List<Long> roIds = baseRoleService.findRoleIdByEmployeeId(ContextUtil.getEmployeeId());
        Long roleId = null;
        //这里这样处理的前提是 每个用户只能有一个角色，为了这个逻辑后台管理必须限制 一个用户只能绑定一个角色
        if (roIds.size() > 0) {
            roleId = roIds.get(0);
        }

        LambdaQueryWrapper<BCareRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BCareRecord::getIsDeleted, CommonConstant.NO_NUMBER);
        queryWrapper.eq(param.getId() != null, BCareRecord::getId, param.getId());
        queryWrapper.eq(StrUtil.isNotBlank(param.getHospitalNumber()), BCareRecord::getHospitalNumber, param.getHospitalNumber());
        queryWrapper.eq(StrUtil.isNotBlank(param.getPatientIdent()), BCareRecord::getPatientIdent, param.getPatientIdent());
        if (param.getStartTime() != null || param.getEndTime() != null) {
            queryWrapper.ge(param.getStartTime() != null, BCareRecord::getMappedTime, param.getStartTime());
            queryWrapper.le(param.getEndTime() != null, BCareRecord::getMappedTime, param.getEndTime());
        } else if (param.getShiftTime() != null && param.getShiftCode() != null) {
            WorkPlanDetailOutVO workPlanDetailOutVO = bWorkPlanService.getShiftTimeSlot(MODEL_NAME_TIME_AXIOS,
                    param.getHospitalNumber(),
                    roleId,
                    param.getShiftTime(), Convert.toLong(param.getShiftCode()));
            if (workPlanDetailOutVO == null) {
                throw new BizException("班次定义为空");
            }
            queryWrapper.ge(workPlanDetailOutVO.getStartTime() != null, BCareRecord::getMappedTime, workPlanDetailOutVO.getStartTime());
            queryWrapper.le(workPlanDetailOutVO.getEndTime() != null, BCareRecord::getMappedTime, workPlanDetailOutVO.getEndTime());
        }
        List<BCareRecord> list = bCareRecordMapper.selectList(queryWrapper);
        return ConvertUtil.convertList(list, CareRecordQueryDto.class);

    }

    /**
     * 根据项目轴与时间轴，形成空值矩阵
     */
    public Map<String, Map<String, CareRecordCellVo>> newMatrix(Map<String, BGenItem> itemBaseAxios, Set<Long> timeBaseAxios) {
        Map<String, Map<String, CareRecordCellVo>> rst = new LinkedHashMap<>();
        int rowIndex = 0;

        if (timeBaseAxios == null || !timeBaseAxios.stream().findFirst().isPresent()) {
            throw new BizException("时间点不能为空");
        }
        Long baseDate = DateUtil.beginOfDay(new Date(timeBaseAxios.stream().findFirst().get())).getTime();
        for (Map.Entry<String, BGenItem> entry : itemBaseAxios.entrySet()) {
            String itemName = entry.getKey();
            BGenItem bGenItem = entry.getValue();
            rowIndex++;
            Map<String, CareRecordCellVo> row = new LinkedHashMap<>();
            row.put(ROW_INDEX, new CareRecordCellVo(COL_TEXT, null, Convert.toStr(rowIndex)));
            row.put(ITEM_NAME, new CareRecordCellVo(COL_TEXT, null, itemName, bGenItem.getOrderNo(), bGenItem.getItemCode()));
            for (Long time : timeBaseAxios) {
                boolean isSecondDay = baseDate != DateUtil.beginOfDay(new Date(time)).getTime();
                row.put(getTimeCode(time, isSecondDay), null);
            }
            rst.put(itemName, row);
        }
        return rst;
    }

    private String getTimeCode(Long time, boolean isSecondDay) {
        if (!isSecondDay) {
            return PRE_A + DateUtil.format(new Date(time), FIELD_HH_MM);
        }
        return PRE_B + DateUtil.format(new Date(time), FIELD_HH_MM);
    }

    private CareRecordColVo getTimeCol(Long time, boolean isSecondDay) {
        String title = DateUtil.format(new Date(time), TITLE_HH_MM);
        String field = DateUtil.format(new Date(time), FIELD_HH_MM);
        if (!isSecondDay) {
            return new CareRecordColVo(COL_TIME, title, PRE_A + field, time);
        }
        return new CareRecordColVo(COL_TIME, title, PRE_B + field, time);
    }


    /**
     * 对《queryBy2Axios》函函数参数进行检
     *
     * @param param
     */
    private void checkParamOfQueryAxios(CareRecordQueryParam param) {
        if (param.getShiftCode() == null && param.getStartTime() == null && param.getEndTime() == null) {
            throw new BizException("班次编码 与‘开始时间’、‘结束时间’二者不能同时有空值");
        }
        if (StrUtil.isBlank(param.getHospitalNumber()) && StrUtil.isBlank(param.getPatientIdent())) {
            throw new BizException(" 病人识别与住院号两者不能同时为空");
        }
    }

    @Override
    public List<CareItemVO> itemQuery(CareItemQueryParam careItemQueryParam) {
        List<BGenItem> parents = bGenItemService.queryItemByPidAndCode(MODE_NAME_OF_CARECZ, careItemQueryParam.getItemCode(), careItemQueryParam.getPatientIdent());
        if (CollectionUtil.isNotEmpty(parents)) {
            List<CareItemVO> careItemVOs = ConvertUtil.convertList(parents, CareItemVO.class);
            List<Long> ids = careItemVOs.stream().map(CareItemVO::getId).collect(Collectors.toList());
            List<BGenItemDetail> list = bGenItemDetailService.getByParentIds(ids);
            Map<Long, List<BGenItemDetail>> map = new HashMap<>();
            if (CollectionUtil.isNotEmpty(list)) {
                map = list.stream().collect(Collectors.groupingBy(v -> v.getGenId()));
            }
            for (CareItemVO vo : careItemVOs) {
                vo.setList(ConvertUtil.convertList(map.get(vo.getId()), CareItemDetailVO.class));
            }
            return careItemVOs;
        }
        return new ArrayList<>();
    }

    /**
     * 新增修改
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer addAndUpdate(List<CareRecordQueryDto> param) {
        int rst = 0;
        if (CollectionUtil.isEmpty(param)) {
            return rst;
        }
        for (CareRecordQueryDto dto : param) {
            BCareRecord bCareRecord = BeanUtil.copyProperties(dto, BCareRecord.class);
            if (dto.getItemId() == null) {
                BGenItem BGenItem = bGenItemService.queryOneByNameNoDel(MODE_NAME_OF_CARECZ, dto.getItemName());
                if (BGenItem != null) {
                    bCareRecord.setItemId(BGenItem.getId());
                    bCareRecord.setItemCode(BGenItem.getItemCode());
                }
            }

            if (bCareRecordMapper.existVaildRecord(dto)) {
                //数据库中存在
                List<BCareRecord> olds = bCareRecordMapper.selectByItemIdAndMapperTime(bCareRecord.getItemId(), bCareRecord.getMappedTime(), bCareRecord.getPatientIdent());
                //验证是否本人修改，提示信息 ：请联系xxx修改
//                if(CollectionUtil.isNotEmpty(olds) && null != SecurityUtil.checkRecordAuth(olds.get(0).getCreateBy() ,olds.get(0).getUpdateBy())){
//                    throw new BizException(SecurityUtil.checkRecordAuth(olds.get(0).getCreateBy() ,olds.get(0).getUpdateBy()));
//                }
                LambdaQueryWrapper<BCareRecord> updateWrapper = new LambdaQueryWrapper<>();
                updateWrapper.eq(BCareRecord::getPatientIdent, dto.getPatientIdent())
                        .eq(BCareRecord::getItemName, dto.getItemName())
                        .eq(BCareRecord::getMappedTime, dto.getMappedTime());
                if (CollectionUtil.isNotEmpty(olds)) {
                    bCareRecord.setId(olds.get(0).getId());
                }
                rst = rst + bCareRecordMapper.update(bCareRecord, updateWrapper);
            } else {
                bCareRecord.setId(null);
                rst = rst + bCareRecordMapper.insert(bCareRecord);
            }

        }
        return rst;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer delCell(CareRecordDelInVO param) {
        LambdaQueryWrapper<BCareRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BCareRecord::getHospitalNumber, param.getHospitalNumber())
                .eq(BCareRecord::getMappedTime, param.getMappedTime())
                .eq(BCareRecord::getItemCode, param.getItemCode());
        List<BCareRecord> list = bCareRecordMapper.selectList(queryWrapper);
//        if(CollectionUtil.isNotEmpty(list)){
        //验证是否本人修改，提示信息 ：请联系xxx修改
//            if(null != SecurityUtil.checkRecordAuth(list.get(0).getCreateBy() ,list.get(0).getUpdateBy())){
//                throw new BizException(SecurityUtil.checkRecordAuth(list.get(0).getCreateBy() ,list.get(0).getUpdateBy()));
//            }
//        }
        return bCareRecordMapper.delete(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRecordStatus(UpdateCareRecordStatusVO updateStatusVO) {
        bCareRecordMapper.updateStatus(updateStatusVO);
    }


    @Override
    public PageOutputDTO<FanShenRecordOutputDTO> queryFanShenRecord(FanShenRecordInputDTO queryInput) {
        String patientIdent = queryInput.getPatientIdent();
        String itemName = queryInput.getItemCode();
        if (StringUtils.isAnyBlank(patientIdent, itemName)) {
            throw new BizException("请输入识别号、住院号和项目名称");
        }
        PageHelper.startPage(queryInput.getPage(), queryInput.getSize());
        List<FanShenRecordOutputDTO> list = bCareRecordMapper.queryFanShenRecord(queryInput);
        PageInfo<FanShenRecordOutputDTO> pageInfo = new PageInfo<>(list);
        return new PageOutputDTO<>(pageInfo.getTotal(), (long) pageInfo.getPages(), (long) pageInfo.getPageNum(), pageInfo.getList());
    }

    @Override
    public List<FanShenRecordOutputDTO> queryAllFanShenRecord(FanShenRecordInputDTO queryInput) {
        String patientIdent = queryInput.getPatientIdent();
        String itemName = queryInput.getItemCode();
        if (StringUtils.isAnyBlank(patientIdent, itemName)) {
            throw new BizException("请输入识别号、住院号和项目名称");
        }
        List<FanShenRecordOutputDTO> list = bCareRecordMapper.queryFanShenRecord(queryInput);
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delCellByTimePiont(String patientIdent, String hospitalNumber, LocalDateTime timePoint) {
        bCareRecordMapper.delCellByTimePiont(patientIdent,hospitalNumber,timePoint);
    }

    @Override
    public Map<String,CareItemVO> itemQueryByPatientIdent(String patientIdent) {
        //List<BGenItem> pat = yxGenItemService.queryItemByPid(YxCareRecordService.MODE_NAME,patientIdent);
        List<BGenItem> sys = bGenItemService.queryItemByPid(MODE_NAME_OF_CARECZ,null);
        Map<String,BGenItem> patmap = new HashMap<>();
//		if(CollectionUtil.isNotEmpty(pat)){
//			patmap = pat.stream().collect(Collectors.toMap(BGenItem::getItemCode,Function.identity()));
//		}
        List<BGenItem> parents = new ArrayList<>();
        parents.addAll(sys);

        if(CollectionUtil.isNotEmpty(parents)){
            List<CareItemVO> careItemVOs = ConvertUtil.convertList(parents,CareItemVO.class);
            List<Long> ids = careItemVOs.stream().map(CareItemVO::getId).collect(Collectors.toList());
            List<BGenItemDetail> list = bGenItemDetailService.getByParentIds(ids);
            Map<Long,List<BGenItemDetail>> map = new HashMap<>();
            if(CollectionUtil.isNotEmpty(list)){
                map = list.stream().collect(Collectors.groupingBy(v->v.getGenId()));
            }
            for(CareItemVO vo : careItemVOs){
                vo.setList(ConvertUtil.convertList(map.get(vo.getId()),CareItemDetailVO.class));
            }
            return careItemVOs.stream().collect(Collectors.toMap(CareItemVO::getItemCode, Function.identity()));
        }
        return new HashMap<>();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveList(List< top.tangyh.lamp.care.vo.query.CareRecordQueryDto> hlcz,Long recordTime, String operator, String patientIdent,Set<String> hlczcodes) {
        //Set<String> names = hlcz.stream().map(YxCareRecordQueryDto::getItemName).collect(Collectors.toSet());
        bCareRecordMapper.deleteOlds(patientIdent,recordTime,hlczcodes,operator);
        if(CollectionUtil.isEmpty(hlcz)){
            return;
        }
        List<BGenItem> yxGenItems = bGenItemService.queryByNameNoDelAll(MODE_NAME_OF_CARECZ, hlczcodes);
        Map<String,List<BGenItem>> map = new HashMap<>();
        if(CollectionUtil.isNotEmpty(yxGenItems)){
            map = yxGenItems.stream().collect(Collectors.groupingBy(BGenItem::getItemName));
        }
        List<BCareRecord> add = new ArrayList<>();
        for (CareRecordQueryDto dto : hlcz) {
            BCareRecord yxCareRecord = BeanUtil.copyProperties(dto, BCareRecord.class);
            yxCareRecord.setMappedTime(recordTime);
            if (dto.getItemId() == null) {
                List<BGenItem> items = map.get(dto.getItemName());
                if (CollectionUtil.isNotEmpty(items)) {
                    yxCareRecord.setItemId(items.get(0).getId());
                    yxCareRecord.setItemCode(items.get(0).getItemCode());
                }
            }
            add.add(yxCareRecord);
        }
        saveBatch(add);
    }
}


